package com.hmdp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.hmdp.config.RabbitMQ.constant.MqConst;
import com.hmdp.config.RabbitMQ.service.RabbirService;
import com.hmdp.config.RedissonConfig;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.HmdpException;
import com.hmdp.utils.RedisIdWorder;
import com.hmdp.utils.ResultCodeEnum;
import com.hmdp.utils.UserHolder;
import com.rabbitmq.client.Channel;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Autowired
    private ISeckillVoucherService seckillVoucherService;
    @Autowired
    private RedisIdWorder redisIdWorder;

    @Autowired
    private IVoucherOrderService IVoucherOrderServicelmpl;
    @Resource
    private RedissonClient redissonClient;

    @Autowired
    private RabbirService rabbitService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

//基于数据库的资格检查

    public long seckillVoucher1(Long voucherId) throws InterruptedException {
//        1.查询优惠券
        SeckillVoucher vouche = seckillVoucherService.getById(voucherId);
        Integer stock1 = vouche.getStock();
        LocalDateTime beginTime = vouche.getBeginTime();
        LocalDateTime endTime = vouche.getEndTime();
//        2.判断开始抢购时间了吗
        if (LocalDateTime.now().isBefore(beginTime) && LocalDateTime.now().isAfter(endTime)) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_notStart);
        }

        RLock lock = redissonClient.getLock("lock:order:" + UserHolder.getUser().getId());
        boolean b = lock.tryLock(1, 10, TimeUnit.SECONDS);
        if (!b) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_lock);
        }
//        3.判断库存充足吗？
        Integer stock = vouche.getStock();
        if (stock <= 0) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_notStock);
        }

//      乐观锁更新之前的检查
        LambdaQueryWrapper<SeckillVoucher> ldbQueryWrapper = new LambdaQueryWrapper();
        ldbQueryWrapper.eq(SeckillVoucher::getVoucherId, voucherId).eq(SeckillVoucher::getStock, stock1);
        SeckillVoucher vouche_Check = seckillVoucherService.getOne(ldbQueryWrapper);

//        synchronized (id.toString().intern()) {}


        VoucherOrder voucherOrder = IVoucherOrderServicelmpl.getVoucherOrder(voucherId, vouche_Check);
        lock.unlock();
        return voucherOrder.getId();

    }

    private static DefaultRedisScript<Long> SECKILL_SCRIPT = null;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }


    private static final ExecutorService SECKILL_Order_EXECUTOR = Executors.newSingleThreadExecutor();


//    @PostConstruct
//    public void init() {
//        //    线程.submit(任务)
//        SECKILL_Order_EXECUTOR.submit(new VocherOrderHandler());
//    }

//    private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
//
//    public class VocherOrderHandler implements Runnable {
//        @Override
//        public void run() {
//            // 任务代码
//            while (true) {
//                try {
////                  1.获得队列中的订单信息。
//                    VoucherOrder voucherOrder = orderTasks.take();
////                  2.保存订单信息到数据库。
//                    handleVoucherOrder(voucherOrder);
//
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//            }
//        }
//    }

    /**
     * 把订单存到数据库。
     *
     * @param voucherOrder
     * @throws InterruptedException 这个订单可以选择进行一人一单和库存不足判断。
     */


    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_ORDER_KEY, durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_ORDER_DIRECT),
            key = {MqConst.ROUTING_ORDER_KEY}
    ))
    private void handleVoucherOrder(VoucherOrder voucherOrder, Message message, Channel channel) throws InterruptedException, IOException {
        if (voucherOrder == null) {
            throw new HmdpException(ResultCodeEnum.DATA_ERROR);
        }

        //        voucherOrder获得订单id  用户id。
        Long userId = voucherOrder.getUserId();


        RLock lock = redissonClient.getLock("lock:order:" + userId);
        boolean b = lock.tryLock(1, 10, TimeUnit.SECONDS);


        if (!b) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_lock);
        }

//        减数据库库存。
        seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherOrder.getVoucherId()).update();

        save(voucherOrder);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        lock.unlock();
    }


    @Override
    public long seckillVoucher(Long voucherId) throws InterruptedException {

        String user_Id = UserHolder.getUser().getId().toString();

//执行lua脚本，返回资格。
        Long seckillStatus = stringRedisTemplate.execute(SECKILL_SCRIPT, Collections.emptyList(), voucherId.toString(), user_Id);


//        根据返回结果返回信息。  1,2表示没有资格   0表示有资格

        if (seckillStatus == 1) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_notStock);
        }
        if (seckillStatus == 2) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_use);
        }

//订单号码
        long order = redisIdWorder.netxId("order");

//如果为0，保存订单。

//        把订单信息放到阻塞队列中。

        VoucherOrder voucherOrder = new VoucherOrder();
        //        获得订单id。
        voucherOrder.setId(order);
//        用户id
        voucherOrder.setUserId(Long.valueOf(user_Id));
        voucherOrder.setVoucherId(voucherId);
//向阻塞队列push订单。
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_ORDER_KEY, voucherOrder);
//        orderTasks.add(voucherOrder);
        return order;
    }

    @Transactional
    public VoucherOrder getVoucherOrder(Long voucherId) {

        //      一人一单。  检查是否优惠券已经使用了。
        Long user_id = UserHolder.getUser().getId();
        Integer count = query().eq("user_id", user_id).eq("voucher_id", voucherId).count();

        if (count != 0) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_use);
        }

        /**
         * 满足资格
         */

//        充足的话扣减库存。
        boolean voucherId1 = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();

        if (!voucherId1) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_notStock);
        }


/**
 *     创建订单
 */

        VoucherOrder voucherOrder = new VoucherOrder();
        //        获得订单id。
        long order = redisIdWorder.netxId("order");
        voucherOrder.setId(order);
//        用户id
        voucherOrder.setUserId(user_id);
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        return voucherOrder;
    }


    /**
     * 基于数据库的方法
     *
     * @param voucherId
     * @param vouche_Check
     * @return
     */
    @Transactional
    public VoucherOrder getVoucherOrder(Long voucherId, SeckillVoucher vouche_Check) {

        //      一人一单。  检查是否优惠券已经使用了。
        Long user_id = UserHolder.getUser().getId();
        Integer count = query().eq("user_id", user_id).eq("voucher_id", voucherId).count();
        if (count != 0) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_use);
        }

        if (vouche_Check == null) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_versionChange);
        }

        /**
         * 满足资格
         */

//        充足的话扣减库存。
        boolean voucherId1 = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();

        if (!voucherId1) {
            throw new HmdpException(ResultCodeEnum.shop_voucher_notStock);
        }


/**
 *     创建订单
 */

        VoucherOrder voucherOrder = new VoucherOrder();
        //        获得订单id。
        long order = redisIdWorder.netxId("order");
        voucherOrder.setId(order);
//        用户id
        voucherOrder.setUserId(user_id);
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        return voucherOrder;
    }

}
